﻿using System;
using System.Reflection.Emit;

namespace IlGenUtil
{
	public static class IlGenExtensions
	{

		#region OpCodes
		public static IlGen<S, Args, Ret> Ble<S, Args, Ret>(this IlGen<Lst<int, Lst<int, S>>, Args, Ret> g, Label l)
		{
			g.il.Emit(OpCodes.Ble, l);
			return new IlGen<S, Args, Ret>(g.il);
		}

		public static IlGen<Lst<int, S>, Args, Ret> Add<S, Args, Ret>(this IlGen<Lst<int, Lst<int, S>>, Args, Ret> g)
		{
			g.il.Emit(OpCodes.Add);
			return new IlGen<Lst<int, S>, Args, Ret>(g.il);
		}

		public static IlGen<Lst<int, S>, Args, Ret> Sub<S, Args, Ret>(this IlGen<Lst<int, Lst<int, S>>, Args, Ret> g)
		{
			g.il.Emit(OpCodes.Sub);
			return new IlGen<Lst<int, S>, Args, Ret>(g.il);
		}


		public static IlGen<S, Args, Ret> Pop<X, S, Args, Ret>(this IlGen<Lst<X, S>, Args, Ret> g)
		{
			g.il.Emit(OpCodes.Pop);
			return new IlGen<S, Args, Ret>(g.il);
		}

		public static IlGen<Lst<X,Lst<X,S>>, Args, Ret> Dup<X, S, Args, Ret>(this IlGen<Lst<X, S>, Args, Ret> g)
		{
			g.il.Emit(OpCodes.Dup);
			return new IlGen<Lst<X, Lst<X, S>>, Args, Ret>(g.il);
		}


		public static IlGen<S, Args, Ret> Stloc<T, S, Args, Ret>(this IlGen<Lst<T, S>, Args, Ret> g, Var<T> var)
		{
			g.il.Emit(OpCodes.Stloc, var.v);
			return new IlGen<S, Args, Ret>(g.il);
		}

		public static IlGen<Lst<T, S>, Args, Ret> Ldloc<T, S, Args, Ret>(this IlGen<S, Args, Ret> g, Var<T> var)
		{
			g.il.Emit(OpCodes.Ldloc, var.v);
			return new IlGen<Lst<T, S>, Args, Ret>(g.il);
		}

		public static IlGen<Lst<int, S>, Args, Ret> Ldc<S, Args, Ret>(this IlGen<S, Args, Ret> g, int x)
		{
			g.il.Emit(OpCodes.Ldc_I4, x);
			return new IlGen<Lst<int, S>, Args, Ret>(g.il);
		}

		public static IlGen<Lst<char, S>, Args, Ret> Ldc<S, Args, Ret>(this IlGen<S, Args, Ret> g, char x)
		{
			g.il.Emit(OpCodes.Ldc_I4, (int) x);
			return new IlGen<Lst<char, S>, Args, Ret>(g.il);
		}

		public static IlGen<Lst<String, S>, Args, Ret> Ldstr<S, Args, Ret>(this IlGen<S, Args, Ret> g, String x)
		{
			g.il.Emit(OpCodes.Ldstr, x);
			return new IlGen<Lst<string, S>, Args, Ret>(g.il);
		}


		public static IlGen<Bottom, Args, Nil> Ret<Args>(this IlGen<Nil, Args, Nil> g)
		{
			g.il.Emit(OpCodes.Ret);
			return new IlGen<Bottom,Args,Nil>(g.il);
		}

		public static IlGen<Bottom, Args, R> Ret<Args, R>(this IlGen<Lst<R, Nil>, Args, R> g)
		{
			g.il.Emit(OpCodes.Ret);
			return new IlGen<Bottom,Args,R> (g.il);
		}

		public static IlGen<Stack, Args, Ret> WriteLine<Stack, Args, Ret>(this IlGen<Stack, Args, Ret> g, String str)
		{
			g.il.EmitWriteLine(str);
			return g;
		}


//		public static IlGen<Stack, Args, Ret> 
		#endregion

		// code blocks
		public static IlGen<S2, Args, Ret> CodeBlock<S1, S2, Args, Ret>(this IlGen<S1, Args, Ret> g, Func<IlGen<S1, Args, Ret>, IlGen<S2, Args, Ret>> f)
		{
			return f(g);
		}
	}
}
